package handler

import (
	"context"
	"crypto/rand"
	"encoding/base64"
	"errors"
	"math"
	"net/http"
	"strconv"
	"tieup/internal/config"
	"tieup/internal/middleware"
	"time"

	"github.com/gin-gonic/gin"
	redis_v8 "github.com/go-redis/redis/v8"
	"github.com/golang-jwt/jwt/v4"
	"golang.org/x/crypto/bcrypt"
	"tieup/internal/model"
	"tieup/internal/service"
	"tieup/pkg/redis"
)

type UserHandler struct {
	userService *service.UserService
	cfg         config.Config
}

func NewUserHandler(userService *service.UserService, cfg config.Config) *UserHandler {
	return &UserHandler{
		userService: userService,
		cfg:         cfg,
	}
}

// RegisterUser 注册用户
func (h *UserHandler) RegisterUser(c *gin.Context) {
	var request struct {
		Username string `json:"username" binding:"required"`
		Email    string `json:"email" binding:"required,email"`
		Password string `json:"password" binding:"required,min=8"`
	}

	if err := c.ShouldBindJSON(&request); err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
		return
	}

	// 密码哈希处理
	hashedPassword, err := bcrypt.GenerateFromPassword([]byte(request.Password), bcrypt.DefaultCost)
	if err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": "Password encryption failed"})
		return
	}

	user := &model.User{
		Username: request.Username,
		Email:    request.Email,
		Password: string(hashedPassword),
	}

	if err := h.userService.CreateUser(c.Request.Context(), user); err != nil {
		c.JSON(http.StatusConflict, gin.H{"error": "Username or email already exists"})
		return
	}

	c.JSON(http.StatusCreated, gin.H{
		"message": "User created successfully",
		"id":      user.ID,
	})
}

// GetProfile 获取用户资料
func (h *UserHandler) GetProfile(c *gin.Context) {
	// 从上下文获取当前用户ID (由AuthMiddleware设置)
	currentUserID, exists := c.Get("userID")
	if !exists {
		c.JSON(http.StatusUnauthorized, gin.H{"error": "User not authenticated"})
		return
	}

	// 获取用户信息
	user, err := h.userService.GetUserByID(c.Request.Context(), currentUserID.(uint))
	if err != nil {
		c.JSON(http.StatusNotFound, gin.H{"error": "User not found"})
		return
	}

	// 返回安全的用户信息（不包含密码）
	c.JSON(http.StatusOK, gin.H{
		"id":        user.ID,
		"username":  user.Username,
		"email":     user.Email,
		"createdAt": user.CreatedAt,
	})
}

// 登录方法使用Redis存储Token
func (h *UserHandler) Login(c *gin.Context) {
	var request struct {
		Email    string `json:"email" binding:"required,email"`
		Password string `json:"password" binding:"required"`
	}

	if err := c.ShouldBindJSON(&request); err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
		return
	}

	// 查询用户
	user, err := h.userService.GetUserByEmail(c.Request.Context(), request.Email)
	if err != nil {
		c.JSON(http.StatusUnauthorized, gin.H{"error": "Invalid credentials"})
		return
	}

	// 验证密码
	if err := bcrypt.CompareHashAndPassword([]byte(user.Password), []byte(request.Password)); err != nil {
		c.JSON(http.StatusUnauthorized, gin.H{"error": "Invalid credentials"})
		return
	}

	// 生成访问令牌和刷新令牌
	accessToken, refreshToken, err := h.generateTokens(user.ID)
	if err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to generate token"})
		return
	}

	// 存储刷新令牌到Redis（用于刷新访问令牌）
	ctx := context.Background()
	err = redis.Client.Set(ctx, "refresh_token:"+refreshToken, user.ID, h.cfg.Auth.RefreshTokenExpiration).Err()
	if err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to store refresh token"})
		return
	}

	// 响应
	c.JSON(http.StatusOK, gin.H{
		"access_token":  accessToken,
		"refresh_token": refreshToken,
		"expires_in":    int(h.cfg.Auth.TokenExpiration / time.Second),
	})
}

// 生成JWT令牌对
func (h *UserHandler) generateTokens(userID uint) (accessToken, refreshToken string, err error) {
	// 生成访问令牌
	accessToken, err = createJWTToken(userID, h.cfg.Auth.SecretKey, h.cfg.Auth.TokenExpiration)
	if err != nil {
		return "", "", err
	}

	// 生成刷新令牌（随机字符串）
	refreshToken, err = generateSecureToken(64)
	if err != nil {
		return "", "", err
	}

	return accessToken, refreshToken, nil
}

// 创建JWT令牌
func createJWTToken(userID uint, secretKey string, expiration time.Duration) (string, error) {
	claims := jwt.MapClaims{
		"sub": userID,
		"exp": time.Now().Add(expiration).Unix(),
		"iat": time.Now().Unix(),
	}
	token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
	return token.SignedString([]byte(secretKey))
}

// 登出方法
func (h *UserHandler) Logout(c *gin.Context) {
	// 调用中间件中的撤销函数
	middleware.InvalidateToken(c, h.cfg.Auth.SecretKey, h.cfg.Auth.TokenExpiration)
	c.JSON(http.StatusOK, gin.H{"message": "Logout successful"})
}

// 刷新令牌方法
func (h *UserHandler) RefreshToken(c *gin.Context) {
	var request struct {
		RefreshToken string `json:"refresh_token" binding:"required"`
	}

	if err := c.ShouldBindJSON(&request); err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
		return
	}

	// 检查刷新令牌是否有效
	ctx := context.Background()
	result, err := redis.Client.Get(ctx, "refresh_token:"+request.RefreshToken).Result()
	if err != nil {
		if err == redis_v8.Nil {
			c.JSON(http.StatusUnauthorized, gin.H{"error": "Invalid refresh token"})
		} else {
			c.JSON(http.StatusInternalServerError, gin.H{"error": "Token service unavailable"})
		}
		return
	}

	// 将字符串结果转换为uint
	userID, err := strconv.ParseUint(result, 10, 64)
	if err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to parse user ID"})
		return
	}

	// 删除旧刷新令牌
	redis.Client.Del(ctx, "refresh_token:"+request.RefreshToken)

	// 生成新的令牌对
	accessToken, newRefreshToken, err := h.generateTokens(uint(userID))
	if err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to generate tokens"})
		return
	}

	// 存储新刷新令牌到Redis
	err = redis.Client.Set(ctx, "refresh_token:"+newRefreshToken, strconv.FormatUint(userID, 10), h.cfg.Auth.RefreshTokenExpiration).Err()
	if err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to store new refresh token"})
		return
	}

	c.JSON(http.StatusOK, gin.H{
		"access_token":  accessToken,
		"refresh_token": newRefreshToken,
		"expires_in":    int(h.cfg.Auth.TokenExpiration / time.Second),
	})
}

// generateSecureToken 生成安全的随机令牌
// length: 返回令牌的字符长度
func generateSecureToken(length int) (string, error) {
	if length <= 0 {
		return "", errors.New("令牌长度必须大于0")
	}

	// 计算需要的随机字节数
	// Base64编码：每3字节转换为4字符
	byteLength := int(math.Ceil(float64(length) * 3 / 4))

	// 生成随机字节
	randomBytes := make([]byte, byteLength)
	if _, err := rand.Read(randomBytes); err != nil {
		return "", err
	}

	// 转换为Base64 URL安全格式
	token := base64.URLEncoding.EncodeToString(randomBytes)

	// 移除Base64填充字符(=)
	token = token[:len(token)-len(token)%4]

	// 截取所需长度
	if len(token) > length {
		token = token[:length]
	} else if len(token) < length {
		// 如果令牌太短，补充随机字符
		extra := make([]byte, length-len(token))
		if _, err := rand.Read(extra); err != nil {
			return "", err
		}
		token += base64.URLEncoding.EncodeToString(extra)[:length-len(token)]
	}

	return token, nil
}
